home *** CD-ROM | disk | FTP | other *** search
/ AmigActive 25 / AACD 25.iso / AACD / Magazine / Online / QMail / source / qmail-send.c < prev    next >
Encoding:
C/C++ Source or Header  |  1997-10-02  |  40.9 KB  |  1,720 lines

  1. #include <sys/types.h>
  2. #include <sys/stat.h>
  3. #include "readwrite.h"
  4. #include "sig.h"
  5. #include "direntry.h"
  6. #include "control.h"
  7. #include "select.h"
  8. #include "open.h"
  9. #include "seek.h"
  10. #include "exit.h"
  11. #include "lock.h"
  12. #include "ndelay.h"
  13. #include "now.h"
  14. #include "getln.h"
  15. #include "substdio.h"
  16. #include "alloc.h"
  17. #include "error.h"
  18. #include "stralloc.h"
  19. #include "str.h"
  20. #include "byte.h"
  21. #include "fmt.h"
  22. #include "scan.h"
  23. #include "case.h"
  24. #include "auto_qmail.h"
  25. #include "trigger.h"
  26. #include "newfield.h"
  27. #include "quote.h"
  28. #include "qmail.h"
  29. #include "qsutil.h"
  30. #include "prioq.h"
  31. #include "constmap.h"
  32. #include "fmtqfn.h"
  33. #include "readsubdir.h"
  34.  
  35. #ifdef __amigaos__
  36. #define timeval amigaos_timeval
  37. #include <exec/types.h>
  38. #include <dos/dos.h>
  39. #include <proto/exec.h>
  40. #undef timeval
  41. #include <sys/signal.h>
  42. #include <stdio.h> /* Needed by ix(_amiga).h. */
  43. /* Hack to find out if include files are for ixemul version 47+ or not. */
  44. #define __pos__
  45. #ifndef CTOBPTR
  46. #define ix_select extselect
  47. #include <ix_amiga.h>
  48. #else
  49. #define __INCLV47__
  50. #include <ix.h>
  51. #endif
  52. #undef __pos__
  53. #endif
  54.  
  55. /* critical timing feature #1: if not triggered, do not busy-loop */
  56. /* critical timing feature #2: if triggered, respond within fixed time */
  57. /* important timing feature: when triggered, respond instantly */
  58. #define SLEEP_TODO 1500 /* check todo/ every 25 minutes in any case */
  59. #define SLEEP_FUZZ 1 /* slop a bit on sleeps to avoid zeno effect */
  60. #define SLEEP_FOREVER 86400 /* absolute maximum time spent in select() */
  61. #define SLEEP_CLEANUP 76431 /* time between cleanups */
  62. #define SLEEP_SYSFAIL 123
  63. #define OSSIFIED 129600 /* 36 hours; _must_ exceed q-q's DEATH (24 hours) */
  64.  
  65. int lifetime = 604800;
  66.  
  67. stralloc percenthack = {0};
  68. struct constmap mappercenthack;
  69. stralloc locals = {0};
  70. struct constmap maplocals;
  71. stralloc redir = {0};
  72. struct constmap mapredir;
  73. stralloc vdoms = {0};
  74. struct constmap mapvdoms;
  75. stralloc envnoathost = {0};
  76. stralloc bouncefrom = {0};
  77. stralloc bouncehost = {0};
  78. stralloc doublebounceto = {0};
  79. stralloc doublebouncehost = {0};
  80.  
  81. char strnum2[FMT_ULONG];
  82. char strnum3[FMT_ULONG];
  83.  
  84. #define CHANNELS 2
  85. char *chanaddr[CHANNELS] = { "local/", "remote/" };
  86. char *channodelmsg[CHANNELS] = {
  87.   "local deliveries will be put on hold\n"
  88. , "remote deliveries will be put on hold\n"
  89. };
  90. char *tochan[CHANNELS] = { " to local ", " to remote " };
  91. int chanfdout[CHANNELS] = { 1, 3 };
  92. int chanfdin[CHANNELS] = { 2, 4 };
  93. int chanskip[CHANNELS] = { 10, 20 };
  94.  
  95. int flagexitasap = 0; void sigterm() { flagexitasap = 1; }
  96. int flagrunasap = 0; void sigalrm() { flagrunasap = 1; }
  97. int flagreadasap = 0; void sighup() { flagreadasap = 1; }
  98.  
  99. void cleandied() { log1("alert: oh no! lost qmail-clean connection! dying...\n");
  100.  flagexitasap = 1; }
  101.  
  102. int flagspawnalive[CHANNELS];
  103. void spawndied(c) int c; { log1("alert: oh no! lost spawn connection! dying...\n");
  104.  flagspawnalive[c] = 0; flagexitasap = 1; }
  105.  
  106. #define REPORTMAX 10000
  107.  
  108. datetime_sec recent;
  109.  
  110.  
  111. /* this file is too long ----------------------------------------- FILENAMES */
  112.  
  113. stralloc fn = {0};
  114. stralloc fn2 = {0};
  115. char fnmake_strnum[FMT_ULONG];
  116.  
  117. void fnmake_init()
  118. {
  119.  while (!stralloc_ready(&fn,FMTQFN)) nomem();
  120.  while (!stralloc_ready(&fn2,FMTQFN)) nomem();
  121. }
  122.  
  123. void fnmake_info(id) unsigned long id; { fn.len = fmtqfn(fn.s,"info/",id,1); }
  124. void fnmake_todo(id) unsigned long id; { fn.len = fmtqfn(fn.s,"todo/",id,0); }
  125. void fnmake_mess(id) unsigned long id; { fn.len = fmtqfn(fn.s,"mess/",id,1); }
  126. void fnmake_foop(id) unsigned long id; { fn.len = fmtqfn(fn.s,"foop/",id,0); }
  127. void fnmake_split(id) unsigned long id; { fn.len = fmtqfn(fn.s,"",id,1); }
  128. void fnmake2_bounce(id) unsigned long id;
  129. { fn2.len = fmtqfn(fn2.s,"bounce/",id,0); }
  130. void fnmake_chanaddr(id,c) unsigned long id; int c;
  131. { fn.len = fmtqfn(fn.s,chanaddr[c],id,1); }
  132.  
  133.  
  134. /* this file is too long ----------------------------------------- REWRITING */
  135.  
  136. stralloc rwline = {0};
  137.  
  138. /* 1 if by land, 2 if by sea, 0 if out of memory. not allowed to barf. */
  139. /* may trash recip. must set up rwline, between a T and a \0. */
  140. int rewrite(recip)
  141. char *recip;
  142. {
  143.   int i;
  144.   int j;
  145.   char *x;
  146.   static stralloc addr = {0};
  147.   static stralloc domain = {0};
  148.  
  149.   if (!stralloc_copys(&rwline,"T")) return 0;
  150.   if (!stralloc_copys(&addr,recip)) return 0;
  151.  
  152.   i = byte_rchr(addr.s,addr.len,'@');
  153.   if (i == addr.len) {
  154.     if (!stralloc_cats(&addr,"@")) return 0;
  155.     if (!stralloc_cat(&addr,&envnoathost)) return 0;
  156.   }
  157.  
  158.   while (constmap(&mappercenthack,addr.s + i + 1,addr.len - i - 1)) {
  159.     j = byte_rchr(addr.s,i,'%');
  160.     if (j == i) break;
  161.     addr.len = i;
  162.     i = j;
  163.     addr.s[i] = '@';
  164.   }
  165.  
  166.   if (x = constmap(&mapredir,addr.s,addr.len))
  167.     if (x[str_chr(x,'@')])
  168.       if (!stralloc_copys(&addr,x)) return 0;
  169.  
  170.   i = byte_rchr(addr.s,addr.len,'@');
  171.   if (!stralloc_copyb(&domain,addr.s + i + 1,addr.len - i - 1)) return 0;
  172.   addr.len = i;
  173.  
  174.   if (constmap(&maplocals,domain.s,domain.len)) {
  175.     if (!stralloc_cat(&rwline,&addr)) return 0;
  176.     if (!stralloc_cats(&rwline,"@")) return 0;
  177.     if (!stralloc_cat(&rwline,&domain)) return 0;
  178.     if (!stralloc_0(&rwline)) return 0;
  179.     return 1;
  180.   }
  181.  
  182.   for (i = 0;i <= domain.len;++i)
  183.     if ((i == 0) || (i == domain.len) || (domain.s[i] == '.'))
  184.       if (x = constmap(&mapvdoms,domain.s + i,domain.len - i)) {
  185.         if (!*x) break;
  186.         if (!stralloc_cats(&rwline,x)) return 0;
  187.         if (!stralloc_cats(&rwline,"-")) return 0;
  188.         if (!stralloc_cat(&rwline,&addr)) return 0;
  189.         if (!stralloc_cats(&rwline,"@")) return 0;
  190.         if (!stralloc_cat(&rwline,&domain)) return 0;
  191.         if (!stralloc_0(&rwline)) return 0;
  192.         return 1;
  193.       }
  194.  
  195.   if (!stralloc_cat(&rwline,&addr)) return 0;
  196.   if (!stralloc_cats(&rwline,"@")) return 0;
  197.   if (!stralloc_cat(&rwline,&domain)) return 0;
  198.   if (!stralloc_0(&rwline)) return 0;
  199.   return 2;
  200. }
  201.  
  202. void senderadd(sa,sender,recip)
  203. stralloc *sa;
  204. char *sender;
  205. char *recip;
  206. {
  207.  int i;
  208.  int j;
  209.  int k;
  210.  
  211.  i = str_len(sender);
  212.  if (i >= 4)
  213.    if (str_equal(sender + i - 4,"-@[]"))
  214.     {
  215.      j = byte_rchr(sender,i - 4,'@');
  216.      k = str_rchr(recip,'@');
  217.      if (recip[k] && (j + 5 <= i))
  218.       {
  219.        /* owner-@host-@[] -> owner-recipbox=reciphost@host */
  220.        while (!stralloc_catb(sa,sender,j)) nomem();
  221.        while (!stralloc_catb(sa,recip,k)) nomem();
  222.        while (!stralloc_cats(sa,"=")) nomem();
  223.        while (!stralloc_cats(sa,recip + k + 1)) nomem();
  224.        while (!stralloc_cats(sa,"@")) nomem();
  225.        while (!stralloc_catb(sa,sender + j + 1,i - 5 - j)) nomem();
  226.        return;
  227.       }
  228.     }
  229.  while (!stralloc_cats(sa,sender)) nomem();
  230. }
  231.  
  232.  
  233. /* this file is too long ---------------------------------------------- INFO */
  234.  
  235. int getinfo(sa,dt,id)
  236. stralloc *sa;
  237. datetime_sec *dt;
  238. unsigned long id;
  239. {
  240.  int fdinfo;
  241.  struct stat st;
  242.  static stralloc line = {0};
  243.  int match;
  244.  substdio ss;
  245.  char buf[128];
  246.  
  247.  fnmake_info(id);
  248.  fdinfo = open_read(fn.s);
  249.  if (fdinfo == -1) return 0;
  250.  if (fstat(fdinfo,&st) == -1) { close(fdinfo); return 0; }
  251.  substdio_fdbuf(&ss,read,fdinfo,buf,sizeof(buf));
  252.  if (getln(&ss,&line,&match,'\0') == -1) { close(fdinfo); return 0; }
  253.  close(fdinfo);
  254.  if (!match) return 0;
  255.  if (line.s[0] != 'F') return 0;
  256.  
  257.  *dt = st.st_mtime;
  258.  while (!stralloc_copys(sa,line.s + 1)) nomem();
  259.  while (!stralloc_0(sa)) nomem();
  260.  return 1;
  261. }
  262.  
  263.  
  264. /* this file is too long ------------------------------------- COMMUNICATION */
  265.  
  266. substdio sstoqc; char sstoqcbuf[1024];
  267. substdio ssfromqc; char ssfromqcbuf[1024];
  268. stralloc comm_buf[CHANNELS] = { {0}, {0} };
  269. int comm_pos[CHANNELS];
  270.  
  271. void comm_init()
  272. {
  273.  int c;
  274.  substdio_fdbuf(&sstoqc,write,5,sstoqcbuf,sizeof(sstoqcbuf));
  275.  substdio_fdbuf(&ssfromqc,read,6,ssfromqcbuf,sizeof(ssfromqcbuf));
  276.  for (c = 0;c < CHANNELS;++c)
  277.    if (ndelay_on(chanfdout[c]) == -1)
  278.    /* this is so stupid: NDELAY semantics should be default on write */
  279.      spawndied(c); /* drastic, but better than risking deadlock */
  280. }
  281.  
  282. int comm_canwrite(c)
  283. int c;
  284. {
  285.  /* XXX: could allow a bigger buffer; say 10 recipients */
  286.  if (comm_buf[c].s && comm_buf[c].len) return 0;
  287.  return 1;
  288. }
  289.  
  290. void comm_write(c,delnum,id,sender,recip)
  291. int c;
  292. int delnum;
  293. unsigned long id;
  294. char *sender;
  295. char *recip;
  296. {
  297.  char ch;
  298.  if (comm_buf[c].s && comm_buf[c].len) return;
  299.  while (!stralloc_copys(&comm_buf[c],"")) nomem();
  300.  ch = delnum;
  301.  while (!stralloc_append(&comm_buf[c],&ch)) nomem();
  302.  fnmake_split(id);
  303.  while (!stralloc_cats(&comm_buf[c],fn.s)) nomem();
  304.  while (!stralloc_0(&comm_buf[c])) nomem();
  305.  senderadd(&comm_buf[c],sender,recip);
  306.  while (!stralloc_0(&comm_buf[c])) nomem();
  307.  while (!stralloc_cats(&comm_buf[c],recip)) nomem();
  308.  while (!stralloc_0(&comm_buf[c])) nomem();
  309.  comm_pos[c] = 0;
  310. }
  311.  
  312. void comm_selprep(nfds,wfds)
  313. int *nfds;
  314. fd_set *wfds;
  315. {
  316.  int c;
  317.  for (c = 0;c < CHANNELS;++c)
  318.    if (flagspawnalive[c])
  319.      if (comm_buf[c].s && comm_buf[c].len)
  320.       {
  321.        FD_SET(chanfdout[c],wfds);
  322.        if (*nfds <= chanfdout[c])
  323.          *nfds = chanfdout[c] + 1;
  324.       }
  325. }
  326.  
  327. void comm_do(wfds)
  328. fd_set *wfds;
  329. {
  330.  int c;
  331.  for (c = 0;c < CHANNELS;++c)
  332.    if (flagspawnalive[c])
  333.      if (comm_buf[c].s && comm_buf[c].len)
  334.        if (FD_ISSET(chanfdout[c],wfds))
  335.         {
  336.          int w;
  337.          int len;
  338.          len = comm_buf[c].len;
  339.          w = write(chanfdout[c],comm_buf[c].s + comm_pos[c],len - comm_pos[c]);
  340.          if (w <= 0)
  341.       {
  342.        if ((w == -1) && (errno == error_pipe))
  343.          spawndied(c);
  344.        else
  345.          continue; /* kernel select() bug; can't avoid busy-looping */
  346.       }
  347.      else
  348.       {
  349.        comm_pos[c] += w;
  350.        if (comm_pos[c] == len)
  351.          comm_buf[c].len = 0;
  352.       }
  353.         }
  354. }
  355.  
  356.  
  357. /* this file is too long ------------------------------------------ CLEANUPS */
  358.  
  359. int flagcleanup; /* if 1, cleanupdir is initialized and ready */
  360. readsubdir cleanupdir;
  361. datetime_sec cleanuptime;
  362.  
  363. void cleanup_init()
  364. {
  365.  flagcleanup = 0;
  366.  cleanuptime = now();
  367. }
  368.  
  369. void cleanup_selprep(wakeup)
  370. datetime_sec *wakeup;
  371. {
  372.  if (flagcleanup) *wakeup = 0;
  373.  if (*wakeup > cleanuptime) *wakeup = cleanuptime;
  374. }
  375.  
  376. void cleanup_do()
  377. {
  378.  char ch;
  379.  struct stat st;
  380.  unsigned long id;
  381.  
  382.  if (!flagcleanup)
  383.   {
  384.    if (recent < cleanuptime) return;
  385.    readsubdir_init(&cleanupdir,"mess",pausedir);
  386.    flagcleanup = 1;
  387.   }
  388.  
  389.  switch(readsubdir_next(&cleanupdir,&id))
  390.   {
  391.    case 1:
  392.      break;
  393.    case 0:
  394.      flagcleanup = 0;
  395.      cleanuptime = recent + SLEEP_CLEANUP;
  396.    default:
  397.      return;
  398.   }
  399.  
  400.  fnmake_mess(id);
  401.  if (stat(fn.s,&st) == -1) return; /* probably qmail-queue deleted it */
  402.  if (recent <= st.st_atime + OSSIFIED) return;
  403.  
  404.  fnmake_info(id);
  405.  if (stat(fn.s,&st) == 0) return;
  406.  if (errno != error_noent) return;
  407.  fnmake_todo(id);
  408.  if (stat(fn.s,&st) == 0) return;
  409.  if (errno != error_noent) return;
  410.  
  411.  fnmake_foop(id);
  412.  if (substdio_putflush(&sstoqc,fn.s,fn.len) == -1) { cleandied(); return; }
  413.  if (substdio_get(&ssfromqc,&ch,1) != 1) { cleandied(); return; }
  414.  if (ch != '+')
  415.    log3("warning: qmail-clean unable to clean up ",fn.s,"\n");
  416. }
  417.  
  418.  
  419. /* this file is too long ----------------------------------- PRIORITY QUEUES */
  420.  
  421. prioq pqdone = {0}; /* -todo +info; HOPEFULLY -local -remote */
  422. prioq pqchan[CHANNELS] = { {0}, {0} };
  423. /* pqchan 0: -todo +info +local ?remote */
  424. /* pqchan 1: -todo +info ?local +remote */
  425. prioq pqfail = {0}; /* stat() failure; has to be pqadded again */
  426.  
  427. void pqadd(id)
  428. unsigned long id;
  429. {
  430.  struct prioq_elt pe;
  431.  struct prioq_elt pechan[CHANNELS];
  432.  int flagchan[CHANNELS];
  433.  struct stat st;
  434.  int c;
  435.  
  436. #define CHECKSTAT if (errno != error_noent) goto fail;
  437.  
  438.  fnmake_info(id);
  439.  if (stat(fn.s,&st) == -1)
  440.   {
  441.    CHECKSTAT
  442.    return; /* someone yanking our chain */
  443.   }
  444.  
  445.  fnmake_todo(id);
  446.  if (stat(fn.s,&st) != -1) return; /* look, ma, dad crashed writing info! */
  447.  CHECKSTAT
  448.  
  449.  for (c = 0;c < CHANNELS;++c)
  450.   {
  451.    fnmake_chanaddr(id,c);
  452.    if (stat(fn.s,&st) == -1) { flagchan[c] = 0; CHECKSTAT }
  453.    else { flagchan[c] = 1; pechan[c].id = id; pechan[c].dt = st.st_mtime; }
  454.   }
  455.  
  456.  for (c = 0;c < CHANNELS;++c)
  457.    if (flagchan[c])
  458.      while (!prioq_insert(&pqchan[c],&pechan[c])) nomem();
  459.  
  460.  for (c = 0;c < CHANNELS;++c) if (flagchan[c]) break;
  461.  if (c == CHANNELS)
  462.   {
  463.    pe.id = id; pe.dt = now();
  464.    while (!prioq_insert(&pqdone,&pe)) nomem();
  465.   }
  466.  
  467.  return;
  468.  
  469.  fail:
  470.  log3("warning: unable to stat ",fn.s,"; will try again later\n");
  471.  pe.id = id; pe.dt = now() + SLEEP_SYSFAIL;
  472.  while (!prioq_insert(&pqfail,&pe)) nomem();
  473. }
  474.  
  475. void pqstart()
  476. {
  477.  readsubdir rs;
  478.  int x;
  479.  unsigned long id;
  480.  
  481.  readsubdir_init(&rs,"info",pausedir);
  482.  
  483.  while (x = readsubdir_next(&rs,&id))
  484.    if (x > 0)
  485.      pqadd(id);
  486. }
  487.  
  488. void pqfinish()
  489. {
  490.  int c;
  491.  struct prioq_elt pe;
  492.  time_t ut[2]; /* XXX: more portable than utimbuf, but still worrisome */
  493.  
  494.  for (c = 0;c < CHANNELS;++c)
  495.    while (prioq_min(&pqchan[c],&pe))
  496.     {
  497.      prioq_delmin(&pqchan[c]);
  498.      fnmake_chanaddr(pe.id,c);
  499.      ut[0] = ut[1] = pe.dt;
  500.      if (utime(fn.s,ut) == -1)
  501.        log3("warning: unable to utime ",fn.s,"; message will be retried too soon\n");
  502.     }
  503. }
  504.  
  505. void pqrun()
  506. {
  507.  int c;
  508.  int i;
  509.  for (c = 0;c < CHANNELS;++c)
  510.    if (pqchan[c].p)
  511.      if (pqchan[c].len)
  512.        for (i = 0;i < pqchan[c].len;++i)
  513.      pqchan[c].p[i].dt = recent;
  514. }
  515.  
  516.  
  517. /* this file is too long ---------------------------------------------- JOBS */
  518.  
  519. struct job
  520.  {
  521.   int refs; /* if 0, this struct is unused */
  522.   unsigned long id;
  523.   int channel;
  524.   datetime_sec retry;
  525.   stralloc sender;
  526.   int numtodo;
  527.   int flaghiteof;
  528.   int flagdying;
  529.  }
  530. ;
  531.  
  532. int numjobs;
  533. struct job *jo;
  534.  
  535. void job_init()
  536. {
  537.  int j;
  538.  while (!(jo = (struct job *) alloc(numjobs * sizeof(struct job)))) nomem();
  539.  for (j = 0;j < numjobs;++j)
  540.   {
  541.    jo[j].refs = 0;
  542.    jo[j].sender.s = 0;
  543.   }
  544. }
  545.  
  546. int job_avail()
  547. {
  548.  int j;
  549.  for (j = 0;j < numjobs;++j) if (!jo[j].refs) return 1;
  550.  return 0;
  551. }
  552.  
  553. int job_open(id,channel)
  554. unsigned long id;
  555. int channel;
  556. {
  557.  int j;
  558.  for (j = 0;j < numjobs;++j) if (!jo[j].refs) break;
  559.  if (j == numjobs) return -1;
  560.  jo[j].refs = 1;
  561.  jo[j].id = id;
  562.  jo[j].channel = channel;
  563.  jo[j].numtodo = 0;
  564.  jo[j].flaghiteof = 0;
  565.  return j;
  566. }
  567.  
  568. void job_close(j)
  569. int j;
  570. {
  571.  struct prioq_elt pe;
  572.  struct stat st;
  573.  
  574.  if (0 < --jo[j].refs) return;
  575.  
  576.  pe.id = jo[j].id;
  577.  pe.dt = jo[j].retry;
  578.  if (jo[j].flaghiteof && !jo[j].numtodo)
  579.   {
  580.    fnmake_chanaddr(jo[j].id,jo[j].channel);
  581.    if (unlink(fn.s) == -1)
  582.     {
  583.      log3("warning: unable to unlink ",fn.s,"; will try again later\n");
  584.      pe.dt = now() + SLEEP_SYSFAIL;
  585.     }
  586.    else
  587.     {
  588.      int c;
  589.      for (c = 0;c < CHANNELS;++c) if (c != jo[j].channel)
  590.       {
  591.        fnmake_chanaddr(jo[j].id,c);
  592.        if (stat(fn.s,&st) == 0) return; /* more channels going */
  593.        if (errno != error_noent)
  594.     {
  595.          log3("warning: unable to stat ",fn.s,"\n");
  596.      break; /* this is the only reason for HOPEFULLY */
  597.     }
  598.       }
  599.      pe.dt = now();
  600.      while (!prioq_insert(&pqdone,&pe)) nomem();
  601.      return;
  602.     }
  603.   }
  604.  
  605.  while (!prioq_insert(&pqchan[jo[j].channel],&pe)) nomem();
  606. }
  607.  
  608.  
  609. /* this file is too long ------------------------------------------- BOUNCES */
  610.  
  611. char *stripvdomprepend(recip)
  612. char *recip;
  613. {
  614.  int i;
  615.  char *domain;
  616.  int domainlen;
  617.  char *prepend;
  618.  
  619.  i = str_rchr(recip,'@');
  620.  if (!recip[i]) return recip;
  621.  domain = recip + i + 1;
  622.  domainlen = str_len(domain);
  623.  
  624.  for (i = 0;i <= domainlen;++i)
  625.    if ((i == 0) || (i == domainlen) || (domain[i] == '.'))
  626.      if (prepend = constmap(&mapvdoms,domain + i,domainlen - i))
  627.       {
  628.        if (!*prepend) break;
  629.        i = str_len(prepend);
  630.        if (str_diffn(recip,prepend,i)) break;
  631.        if (recip[i] != '-') break;
  632.        return recip + i + 1;
  633.       }
  634.  return recip;
  635. }
  636.  
  637. stralloc bouncetext = {0};
  638.  
  639. void addbounce(id,recip,report)
  640. unsigned long id;
  641. char *recip;
  642. char *report;
  643. {
  644.  int fd;
  645.  int pos;
  646.  int w;
  647.  while (!stralloc_copys(&bouncetext,"<")) nomem();
  648.  while (!stralloc_cats(&bouncetext,stripvdomprepend(recip))) nomem();
  649.  for (pos = 0;pos < bouncetext.len;++pos)
  650.    if (bouncetext.s[pos] == '\n')
  651.      bouncetext.s[pos] = '_';
  652.  while (!stralloc_cats(&bouncetext,">:\n")) nomem();
  653.  while (!stralloc_cats(&bouncetext,report)) nomem();
  654.  if (report[0])
  655.    if (report[str_len(report) - 1] != '\n')
  656.      while (!stralloc_cats(&bouncetext,"\n")) nomem();
  657.  for (pos = bouncetext.len - 2;pos > 0;--pos)
  658.    if (bouncetext.s[pos] == '\n')
  659.      if (bouncetext.s[pos - 1] == '\n')
  660.        bouncetext.s[pos] = '/';
  661.  while (!stralloc_cats(&bouncetext,"\n")) nomem();
  662.  fnmake2_bounce(id);
  663.  for (;;)
  664.   {
  665.    fd = open_append(fn2.s);
  666.    if (fd != -1) break;
  667.    log1("alert: unable to append to bounce message; HELP! sleeping...\n");
  668.    sleep(10);
  669.   }
  670.  pos = 0;
  671.  while (pos < bouncetext.len)
  672.   {
  673.    w = write(fd,bouncetext.s + pos,bouncetext.len - pos);
  674.    if (w <= 0)
  675.     {
  676.      log1("alert: unable to append to bounce message; HELP! sleeping...\n");
  677.      sleep(10);
  678.     }
  679.    else
  680.      pos += w;
  681.   }
  682.  close(fd);
  683. }
  684.  
  685. int injectbounce(id)
  686. unsigned long id;
  687. {
  688.  struct qmail qqt;
  689.  struct stat st;
  690.  char *bouncesender;
  691.  char *bouncerecip;
  692.  int r;
  693.  int fd;
  694.  substdio ssread;
  695.  char buf[128];
  696.  char inbuf[128];
  697.  static stralloc sender = {0};
  698.  static stralloc quoted = {0};
  699.  datetime_sec birth;
  700.  unsigned long qp;
  701.  
  702.  if (!getinfo(&sender,&birth,id)) return 0; /* XXX: print warning */
  703.  
  704.  /* owner-@host-@[] -> owner-@host */
  705.  if (sender.len >= 5)
  706.    if (str_equal(sender.s + sender.len - 5,"-@[]"))
  707.     {
  708.      sender.len -= 4;
  709.      sender.s[sender.len - 1] = 0;
  710.     }
  711.  
  712.  fnmake2_bounce(id);
  713.  fnmake_mess(id);
  714.  if (stat(fn2.s,&st) == -1)
  715.   {
  716.    if (errno == error_noent)
  717.      return 1;
  718.    log3("warning: unable to stat ",fn2.s,"\n");
  719.    return 0;
  720.   }
  721.  if (str_equal(sender.s,"#@[]"))
  722.    log3("triple bounce: discarding ",fn2.s,"\n");
  723.  else
  724.   {
  725.    if (qmail_open(&qqt) == -1)
  726.     { log1("warning: unable to start qmail-queue, will try later\n"); return 0; }
  727.    qp = qmail_qp(&qqt);
  728.  
  729.    if (*sender.s) { bouncesender = ""; bouncerecip = sender.s; }
  730.    else { bouncesender = "#@[]"; bouncerecip = doublebounceto.s; }
  731.  
  732.    while (!newfield_datemake(now())) nomem();
  733.    qmail_put(&qqt,newfield_date.s,newfield_date.len);
  734.    qmail_puts(&qqt,"From: ");
  735.    while (!quote("ed,&bouncefrom)) nomem();
  736.    qmail_put(&qqt,quoted.s,quoted.len);
  737.    qmail_puts(&qqt,"@");
  738.    qmail_put(&qqt,bouncehost.s,bouncehost.len);
  739.    qmail_puts(&qqt,"\nTo: ");
  740.    while (!quote2("ed,bouncerecip)) nomem();
  741.    qmail_put(&qqt,quoted.s,quoted.len);
  742.    qmail_puts(&qqt,"\n\
  743. Subject: failure notice\n\
  744. \n\
  745. Hi. This is the qmail-send program at ");
  746.    qmail_put(&qqt,bouncehost.s,bouncehost.len);
  747.    qmail_puts(&qqt,*sender.s ? ".\n\
  748. I'm afraid I wasn't able to deliver your message to the following addresses.\n\
  749. This is a permanent error; I've given up. Sorry it didn't work out.\n\
  750. \n\
  751. " : ".\n\
  752. I tried to deliver a bounce message to this address, but the bounce bounced!\n\
  753. \n\
  754. ");
  755.  
  756.    fd = open_read(fn2.s);
  757.    if (fd == -1)
  758.      qmail_fail(&qqt);
  759.    else
  760.     {
  761.      substdio_fdbuf(&ssread,read,fd,inbuf,sizeof(inbuf));
  762.      while ((r = substdio_get(&ssread,buf,sizeof(buf))) > 0)
  763.        qmail_put(&qqt,buf,r);
  764.      close(fd);
  765.      if (r == -1)
  766.        qmail_fail(&qqt);
  767.     }
  768.  
  769.    qmail_puts(&qqt,*sender.s ? "--- Below this line is a copy of the message.\n\n" : "--- Below this line is the original bounce.\n\n");
  770.    qmail_puts(&qqt,"Return-Path: <");
  771.    while (!quote2("ed,sender.s)) nomem();
  772.    qmail_put(&qqt,quoted.s,quoted.len);
  773.    qmail_puts(&qqt,">\n");
  774.  
  775.    fd = open_read(fn.s);
  776.    if (fd == -1)
  777.      qmail_fail(&qqt);
  778.    else
  779.     {
  780.      substdio_fdbuf(&ssread,read,fd,inbuf,sizeof(inbuf));
  781.      while ((r = substdio_get(&ssread,buf,sizeof(buf))) > 0)
  782.        qmail_put(&qqt,buf,r);
  783.      close(fd);
  784.      if (r == -1)
  785.        qmail_fail(&qqt);
  786.     }
  787.  
  788.    qmail_from(&qqt,bouncesender);
  789.    qmail_to(&qqt,bouncerecip);
  790.    if (qmail_close(&qqt))
  791.     { log1("warning: trouble injecting bounce message, will try later\n"); return 0; }
  792.  
  793.    strnum2[fmt_ulong(strnum2,id)] = 0;
  794.    log2("bounce msg ",strnum2);
  795.    strnum2[fmt_ulong(strnum2,qp)] = 0;
  796.    log3(" qp ",strnum2,"\n");
  797.   }
  798.  if (unlink(fn2.s) == -1)
  799.   {
  800.    log3("warning: unable to unlink ",fn2.s,"\n");
  801.    return 0;
  802.   }
  803.  return 1;
  804. }
  805.  
  806.  
  807. /* this file is too long ---------------------------------------- DELIVERIES */
  808.  
  809. struct del
  810.  {
  811.   int used;
  812.   int j;
  813.   unsigned long delid;
  814.   seek_pos mpos;
  815.   stralloc recip;
  816.  }
  817. ;
  818.  
  819. unsigned long masterdelid = 1;
  820. unsigned int concurrency[CHANNELS] = { 10, 20 };
  821. struct del *d[CHANNELS];
  822. stralloc dline[CHANNELS];
  823. char delbuf[2048];
  824.  
  825. void del_init()
  826. {
  827.  int c;
  828.  int i;
  829.  for (c = 0;c < CHANNELS;++c)
  830.   {
  831.    flagspawnalive[c] = 1;
  832.    while (!(d[c] = (struct del *) alloc(concurrency[c] * sizeof(struct del))))
  833.      nomem();
  834.    for (i = 0;i < concurrency[c];++i)
  835.     { d[c][i].used = 0; d[c][i].recip.s = 0; }
  836.    dline[c].s = 0;
  837.    while (!stralloc_copys(&dline[c],"")) nomem();
  838.   }
  839. }
  840.  
  841. int del_canexit()
  842. {
  843.  int i;
  844.  int c;
  845.  for (c = 0;c < CHANNELS;++c)
  846.    if (flagspawnalive[c]) /* if dead, nothing we can do about its jobs */
  847.      for (i = 0;i < concurrency[c];++i)
  848.        if (d[c][i].used) return 0;
  849.  return 1;
  850. }
  851.  
  852. static int del_lastsaid = 0;
  853.  
  854. void del_saywhynoexit()
  855. {
  856.  int i;
  857.  int c;
  858.  int n;
  859.  n = 0;
  860.  for (c = 0;c < CHANNELS;++c)
  861.    if (flagspawnalive[c])
  862.      for (i = 0;i < concurrency[c];++i)
  863.        if (d[c][i].used)
  864.      ++n;
  865.  if (!del_lastsaid || (n < del_lastsaid))
  866.   {
  867.    strnum2[fmt_ulong(strnum2,(unsigned long) n)] = 0;
  868.    log3("number of deliveries left before exiting: ",strnum2,"\n");
  869.    del_lastsaid = n;
  870.   }
  871. }
  872.  
  873. int del_avail(c)
  874. int c;
  875. {
  876.  int i;
  877.  
  878.  if (!flagspawnalive[c]) return 0;
  879.  if (!comm_canwrite(c)) return 0;
  880.  for (i = 0;i < concurrency[c];++i) if (!d[c][i].used) return 1;
  881.  return 0;
  882. }
  883.  
  884. void del_start(j,mpos,recip)
  885. int j;
  886. seek_pos mpos;
  887. char *recip;
  888. {
  889.  int i;
  890.  int c;
  891.  
  892.  c = jo[j].channel;
  893.  if (!flagspawnalive[c]) return;
  894.  if (!comm_canwrite(c)) return;
  895.  
  896.  for (i = 0;i < concurrency[c];++i) if (!d[c][i].used) break;
  897.  if (i == concurrency[c]) return;
  898.  
  899.  if (!stralloc_copys(&d[c][i].recip,recip)) { nomem(); return; }
  900.  if (!stralloc_0(&d[c][i].recip)) { nomem(); return; }
  901.  d[c][i].j = j; ++jo[j].refs;
  902.  d[c][i].delid = masterdelid++;
  903.  d[c][i].mpos = mpos;
  904.  d[c][i].used = 1;
  905.  
  906.  comm_write(c,i,jo[j].id,jo[j].sender.s,recip);
  907.  
  908.  strnum2[fmt_ulong(strnum2,d[c][i].delid)] = 0;
  909.  strnum3[fmt_ulong(strnum3,jo[j].id)] = 0;
  910.  log2("starting delivery ",strnum2);
  911.  log3(": msg ",strnum3,tochan[c]);
  912.  logsafe(recip);
  913.  log1("\n");
  914. }
  915.  
  916. void markdone(c,id,pos)
  917. int c;
  918. unsigned long id;
  919. seek_pos pos;
  920. {
  921.  struct stat st;
  922.  int fd;
  923.  fnmake_chanaddr(id,c);
  924.  for (;;)
  925.   {
  926.    fd = open_write(fn.s);
  927.    if (fd == -1) break;
  928.    if (fstat(fd,&st) == -1) { close(fd); break; }
  929.    if (seek_set(fd,pos) == -1) { close(fd); break; }
  930.    if (write(fd,"D",1) != 1) { close(fd); break; }
  931.    /* further errors -> double delivery without us knowing about it, oh well */
  932.    close(fd);
  933.    return;
  934.   }
  935.  log3("warning: trouble marking ",fn.s,"; message will be delivered twice!\n");
  936. }
  937.  
  938. void del_dochan(c)
  939. int c;
  940. {
  941.  int r;
  942.  char ch;
  943.  int i;
  944.  int delnum;
  945.  r = read(chanfdin[c],delbuf,sizeof(delbuf));
  946.  if (r == -1) return;
  947.  if (r == 0) { spawndied(c); return; }
  948.  for (i = 0;i < r;++i)
  949.   {
  950.    ch = delbuf[i];
  951.    while (!stralloc_append(&dline[c],&ch)) nomem();
  952.    if (dline[c].len > REPORTMAX)
  953.      dline[c].len = REPORTMAX;
  954.      /* qmail-lspawn and qmail-rspawn are responsible for keeping it short */
  955.      /* but from a security point of view, we don't trust rspawn */
  956.    if (!ch && (dline[c].len > 1))
  957.     {
  958.      delnum = (unsigned int) (unsigned char) dline[c].s[0];
  959.      if ((delnum < 0) || (delnum >= concurrency[c]) || !d[c][delnum].used)
  960.        log1("warning: internal error: delivery report out of range\n");
  961.      else
  962.       {
  963.        strnum3[fmt_ulong(strnum3,d[c][delnum].delid)] = 0;
  964.        if (dline[c].s[1] == 'Z')
  965.      if (jo[d[c][delnum].j].flagdying)
  966.       {
  967.        dline[c].s[1] = 'D';
  968.        --dline[c].len;
  969.        while (!stralloc_cats(&dline[c],"I'm not going to try again; this message has been in the queue too long.\n")) nomem();
  970.        while (!stralloc_0(&dline[c])) nomem();
  971.       }
  972.        switch(dline[c].s[1])
  973.     {
  974.      case 'K':
  975.        log3("delivery ",strnum3,": success: ");
  976.        logsafe(dline[c].s + 2);
  977.        log1("\n");
  978.        markdone(c,jo[d[c][delnum].j].id,d[c][delnum].mpos);
  979.        --jo[d[c][delnum].j].numtodo;
  980.        break;
  981.      case 'Z':
  982.        log3("delivery ",strnum3,": deferral: ");
  983.        logsafe(dline[c].s + 2);
  984.        log1("\n");
  985.        break;
  986.      case 'D':
  987.        log3("delivery ",strnum3,": failure: ");
  988.        logsafe(dline[c].s + 2);
  989.        log1("\n");
  990.        addbounce(jo[d[c][delnum].j].id,d[c][delnum].recip.s,dline[c].s + 2);
  991.        markdone(c,jo[d[c][delnum].j].id,d[c][delnum].mpos);
  992.        --jo[d[c][delnum].j].numtodo;
  993.        break;
  994.      default:
  995.        log3("delivery ",strnum3,": report mangled, will defer\n");
  996.     }
  997.        job_close(d[c][delnum].j);
  998.        d[c][delnum].used = 0;
  999.       }
  1000.      dline[c].len = 0;
  1001.     }
  1002.   }
  1003. }
  1004.  
  1005. void del_selprep(nfds,rfds)
  1006. int *nfds;
  1007. fd_set *rfds;
  1008. {
  1009.  int c;
  1010.  for (c = 0;c < CHANNELS;++c)
  1011.    if (flagspawnalive[c])
  1012.     {
  1013.      FD_SET(chanfdin[c],rfds);
  1014.      if (*nfds <= chanfdin[c])
  1015.        *nfds = chanfdin[c] + 1;
  1016.     }
  1017. }
  1018.  
  1019. void del_do(rfds)
  1020. fd_set *rfds;
  1021. {
  1022.  int c;
  1023.  for (c = 0;c < CHANNELS;++c)
  1024.    if (flagspawnalive[c])
  1025.      if (FD_ISSET(chanfdin[c],rfds))
  1026.        del_dochan(c);
  1027. }
  1028.  
  1029.  
  1030. /* this file is too long -------------------------------------------- PASSES */
  1031.  
  1032. struct
  1033.  {
  1034.   unsigned long id; /* if 0, need a new pass */
  1035.   int j; /* defined if id; job number */
  1036.   int fd; /* defined if id; reading from {local,remote} */
  1037.   seek_pos mpos; /* defined if id; mark position */
  1038.   substdio ss;
  1039.   char buf[128];
  1040.  }
  1041. pass[CHANNELS];
  1042.  
  1043. void pass_init()
  1044. {
  1045.  int c;
  1046.  for (c = 0;c < CHANNELS;++c) pass[c].id = 0;
  1047. }
  1048.  
  1049. void pass_selprep(wakeup)
  1050. datetime_sec *wakeup;
  1051. {
  1052.  int c;
  1053.  struct prioq_elt pe;
  1054.  if (flagexitasap) return;
  1055.  for (c = 0;c < CHANNELS;++c)
  1056.    if (pass[c].id)
  1057.      if (del_avail(c))
  1058.       { *wakeup = 0; return; }
  1059.  if (job_avail())
  1060.    for (c = 0;c < CHANNELS;++c)
  1061.      if (!pass[c].id)
  1062.        if (prioq_min(&pqchan[c],&pe))
  1063.          if (*wakeup > pe.dt)
  1064.            *wakeup = pe.dt;
  1065.  if (prioq_min(&pqfail,&pe))
  1066.    if (*wakeup > pe.dt)
  1067.      *wakeup = pe.dt;
  1068.  if (prioq_min(&pqdone,&pe))
  1069.    if (*wakeup > pe.dt)
  1070.      *wakeup = pe.dt;
  1071. }
  1072.  
  1073. static datetime_sec squareroot(x) /* result^2 <= x < (result + 1)^2 */
  1074. datetime_sec x; /* assuming: >= 0 */
  1075. {
  1076.  datetime_sec y;
  1077.  datetime_sec yy;
  1078.  datetime_sec y21;
  1079.  int j;
  1080.  
  1081.  y = 0; yy = 0;
  1082.  for (j = 15;j >= 0;--j)
  1083.   {
  1084.    y21 = (y << (j + 1)) + (1 << (j + j));
  1085.    if (y21 <= x - yy) { y += (1 << j); yy += y21; }
  1086.   }
  1087.  return y;
  1088. }
  1089.  
  1090. datetime_sec nextretry(birth,c)
  1091. datetime_sec birth;
  1092. int c;
  1093. {
  1094.  int n;
  1095.  
  1096.  if (birth > recent) n = 0;
  1097.  else n = squareroot(recent - birth); /* no need to add fuzz to recent */
  1098.  n += chanskip[c];
  1099.  return birth + n * n;
  1100. }
  1101.  
  1102. void pass_dochan(c)
  1103. int c;
  1104. {
  1105.  datetime_sec birth;
  1106.  struct prioq_elt pe;
  1107.  static stralloc line = {0};
  1108.  int match;
  1109.  
  1110.  if (flagexitasap) return;
  1111.  
  1112.  if (!pass[c].id)
  1113.   {
  1114.    if (!job_avail()) return;
  1115.    if (!prioq_min(&pqchan[c],&pe)) return;
  1116.    if (pe.dt > recent) return;
  1117.    fnmake_chanaddr(pe.id,c);
  1118.  
  1119.    prioq_delmin(&pqchan[c]);
  1120.    pass[c].mpos = 0;
  1121.    pass[c].fd = open_read(fn.s);
  1122.    if (pass[c].fd == -1) goto trouble;
  1123.    if (!getinfo(&line,&birth,pe.id)) { close(pass[c].fd); goto trouble; }
  1124.    pass[c].id = pe.id;
  1125.    substdio_fdbuf(&pass[c].ss,read,pass[c].fd,pass[c].buf,sizeof(pass[c].buf));
  1126.    pass[c].j = job_open(pe.id,c);
  1127.    jo[pass[c].j].retry = nextretry(birth,c);
  1128.    jo[pass[c].j].flagdying = (recent > birth + lifetime);
  1129.    while (!stralloc_copy(&jo[pass[c].j].sender,&line)) nomem();
  1130.   }
  1131.  
  1132.  if (!del_avail(c)) return;
  1133.  
  1134.  if (getln(&pass[c].ss,&line,&match,'\0') == -1)
  1135.   {
  1136.    fnmake_chanaddr(pass[c].id,c);
  1137.    log3("warning: trouble reading ",fn.s,"; will try again later\n");
  1138.    close(pass[c].fd);
  1139.    job_close(pass[c].j);
  1140.    pass[c].id = 0;
  1141.    return;
  1142.   }
  1143.  if (!match)
  1144.   {
  1145.    close(pass[c].fd);
  1146.    jo[pass[c].j].flaghiteof = 1;
  1147.    job_close(pass[c].j);
  1148.    pass[c].id = 0;
  1149.    return;
  1150.   }
  1151.  switch(line.s[0])
  1152.   {
  1153.    case 'T':
  1154.      ++jo[pass[c].j].numtodo;
  1155.      del_start(pass[c].j,pass[c].mpos,line.s + 1);
  1156.      break;
  1157.    case 'D':
  1158.      break;
  1159.    default:
  1160.      fnmake_chanaddr(pass[c].id,c);
  1161.      log3("warning: unknown record type in ",fn.s,"!\n");
  1162.      close(pass[c].fd);
  1163.      job_close(pass[c].j);
  1164.      pass[c].id = 0;
  1165.      return;
  1166.   }
  1167.  
  1168.  pass[c].mpos += line.len;
  1169.  return;
  1170.  
  1171.  trouble:
  1172.  log3("warning: trouble opening ",fn.s,"; will try again later\n");
  1173.  pe.dt = recent + SLEEP_SYSFAIL;
  1174.  while (!prioq_insert(&pqchan[c],&pe)) nomem();
  1175. }
  1176.  
  1177. void messdone(id)
  1178. unsigned long id;
  1179. {
  1180.  char ch;
  1181.  int c;
  1182.  struct prioq_elt pe;
  1183.  struct stat st;
  1184.  
  1185.  for (c = 0;c < CHANNELS;++c)
  1186.   {
  1187.    fnmake_chanaddr(id,c);
  1188.    if (stat(fn.s,&st) == 0) return; /* false alarm; consequence of HOPEFULLY */
  1189.    if (errno != error_noent)
  1190.     {
  1191.      log3("warning: unable to stat ",fn.s,"; will try again later\n");
  1192.      goto fail;
  1193.     }
  1194.   }
  1195.  
  1196.  fnmake_todo(id);
  1197.  if (stat(fn.s,&st) == 0) return;
  1198.  if (errno != error_noent)
  1199.   {
  1200.    log3("warning: unable to stat ",fn.s,"; will try again later\n");
  1201.    goto fail;
  1202.   }
  1203.  
  1204.  fnmake_info(id);
  1205.  if (stat(fn.s,&st) == -1)
  1206.   {
  1207.    if (errno == error_noent) return;
  1208.    log3("warning: unable to stat ",fn.s,"; will try again later\n");
  1209.    goto fail;
  1210.   }
  1211.  
  1212.  /* -todo +info -local -remote ?bounce */
  1213.  if (!injectbounce(id))
  1214.    goto fail; /* injectbounce() produced error message */
  1215.  
  1216.  strnum3[fmt_ulong(strnum3,id)] = 0;
  1217.  log3("end msg ",strnum3,"\n");
  1218.  
  1219.  /* -todo +info -local -remote -bounce */
  1220.  fnmake_info(id);
  1221.  if (unlink(fn.s) == -1)
  1222.   {
  1223.    log3("warning: unable to unlink ",fn.s,"; will try again later\n");
  1224.    goto fail;
  1225.   }
  1226.  
  1227.  /* -todo -info -local -remote -bounce; we can relax */
  1228.  fnmake_foop(id);
  1229.  if (substdio_putflush(&sstoqc,fn.s,fn.len) == -1) { cleandied(); return; }
  1230.  if (substdio_get(&ssfromqc,&ch,1) != 1) { cleandied(); return; }
  1231.  if (ch != '+')
  1232.    log3("warning: qmail-clean unable to clean up ",fn.s,"\n");
  1233.  
  1234.  return;
  1235.  
  1236.  fail:
  1237.  pe.id = id; pe.dt = now() + SLEEP_SYSFAIL;
  1238.  while (!prioq_insert(&pqdone,&pe)) nomem();
  1239. }
  1240.  
  1241. void pass_do()
  1242. {
  1243.  int c;
  1244.  struct prioq_elt pe;
  1245.  
  1246.  for (c = 0;c < CHANNELS;++c) pass_dochan(c);
  1247.  if (prioq_min(&pqfail,&pe))
  1248.    if (pe.dt <= recent)
  1249.     {
  1250.      prioq_delmin(&pqfail);
  1251.      pqadd(pe.id);
  1252.     }
  1253.  if (prioq_min(&pqdone,&pe))
  1254.    if (pe.dt <= recent)
  1255.     {
  1256.      prioq_delmin(&pqdone);
  1257.      messdone(pe.id);
  1258.     }
  1259. }
  1260.  
  1261.  
  1262. /* this file is too long ---------------------------------------------- TODO */
  1263.  
  1264. datetime_sec nexttodorun;
  1265. DIR *tododir; /* if 0, have to opendir again */
  1266. stralloc todoline = {0};
  1267. char todobuf[SUBSTDIO_INSIZE];
  1268. char todobufinfo[512];
  1269. char todobufchan[CHANNELS][1024];
  1270.  
  1271. void todo_init()
  1272. {
  1273.  tododir = 0;
  1274.  nexttodorun = now();
  1275.  trigger_set();
  1276. }
  1277.  
  1278. void todo_selprep(nfds,rfds,wakeup)
  1279. int *nfds;
  1280. fd_set *rfds;
  1281. datetime_sec *wakeup;
  1282. {
  1283.  if (flagexitasap) return;
  1284.  trigger_selprep(nfds,rfds);
  1285.  if (tododir) *wakeup = 0;
  1286.  if (*wakeup > nexttodorun) *wakeup = nexttodorun;
  1287. }
  1288.  
  1289. #ifndef __amigaos
  1290. void todo_do(rfds)
  1291. fd_set *rfds;
  1292. #else
  1293. void todo_do (unsigned long signals)
  1294. #endif
  1295. {
  1296.  struct stat st;
  1297.  substdio ss; int fd;
  1298.  substdio ssinfo; int fdinfo;
  1299.  substdio sschan[CHANNELS];
  1300.  int fdchan[CHANNELS];
  1301.  int flagchan[CHANNELS];
  1302.  struct prioq_elt pe;
  1303.  char ch;
  1304.  int match;
  1305.  unsigned long id;
  1306.  unsigned int len;
  1307.  direntry *d;
  1308.  int c;
  1309.  unsigned long uid;
  1310.  unsigned long pid;
  1311.  
  1312.  fd = -1;
  1313.  fdinfo = -1;
  1314.  for (c = 0;c < CHANNELS;++c) fdchan[c] = -1;
  1315.  
  1316.  if (flagexitasap) return;
  1317.  
  1318.  if (!tododir)
  1319.   {
  1320. #ifndef __amigaos__
  1321.    if (!trigger_pulled(rfds))
  1322. #else
  1323.    if (!trigger_pulled (signals))
  1324. #endif
  1325.      if (recent < nexttodorun)
  1326.        return;
  1327.    trigger_set();
  1328.    tododir = opendir("todo");
  1329.    if (!tododir)
  1330.     {
  1331.      pausedir("todo");
  1332.      return;
  1333.     }
  1334.    nexttodorun = recent + SLEEP_TODO;
  1335.   }
  1336.  
  1337.  d = readdir(tododir);
  1338.  if (!d)
  1339.   {
  1340.    closedir(tododir);
  1341.    tododir = 0;
  1342.    return;
  1343.   }
  1344.  if (str_equal(d->d_name,".")) return;
  1345.  if (str_equal(d->d_name,"..")) return;
  1346.  len = scan_ulong(d->d_name,&id);
  1347.  if (!len || d->d_name[len]) return;
  1348.  
  1349.  fnmake_todo(id);
  1350.  
  1351.  fd = open_read(fn.s);
  1352.  if (fd == -1) { log3("warning: unable to open ",fn.s,"\n"); return; }
  1353.  
  1354.  fnmake_mess(id);
  1355.  /* just for the statistics */
  1356.  if (stat(fn.s,&st) == -1)
  1357.   { log3("warning: unable to stat ",fn.s,"\n"); goto fail; }
  1358.  
  1359.  for (c = 0;c < CHANNELS;++c)
  1360.   {
  1361.    fnmake_chanaddr(id,c);
  1362.    if (unlink(fn.s) == -1) if (errno != error_noent)
  1363.     { log3("warning: unable to unlink ",fn.s,"\n"); goto fail; }
  1364.   }
  1365.  
  1366.  fnmake_info(id);
  1367.  if (unlink(fn.s) == -1) if (errno != error_noent)
  1368.   { log3("warning: unable to unlink ",fn.s,"\n"); goto fail; }
  1369.  
  1370.  fdinfo = open_excl(fn.s);
  1371.  if (fdinfo == -1)
  1372.   { log3("warning: unable to create ",fn.s,"\n"); goto fail; }
  1373.  
  1374.  strnum3[fmt_ulong(strnum3,id)] = 0;
  1375.  log3("new msg ",strnum3,"\n");
  1376.  
  1377.  for (c = 0;c < CHANNELS;++c) flagchan[c] = 0;
  1378.  
  1379.  substdio_fdbuf(&ss,read,fd,todobuf,sizeof(todobuf));
  1380.  substdio_fdbuf(&ssinfo,write,fdinfo,todobufinfo,sizeof(todobufinfo));
  1381.  
  1382.  uid = 0;
  1383.  pid = 0;
  1384.  
  1385.  for (;;)
  1386.   {
  1387.    if (getln(&ss,&todoline,&match,'\0') == -1)
  1388.     {
  1389.      /* perhaps we're out of memory, perhaps an I/O error */
  1390.      fnmake_todo(id);
  1391.      log3("warning: trouble reading ",fn.s,"\n"); goto fail;
  1392.     }
  1393.    if (!match) break;
  1394.  
  1395.    switch(todoline.s[0])
  1396.     {
  1397.      case 'u':
  1398.        scan_ulong(todoline.s + 1,&uid);
  1399.        break;
  1400.      case 'p':
  1401.        scan_ulong(todoline.s + 1,&pid);
  1402.        break;
  1403.      case 'F':
  1404.        if (substdio_putflush(&ssinfo,todoline.s,todoline.len) == -1)
  1405.     {
  1406.      fnmake_info(id);
  1407.          log3("warning: trouble writing to ",fn.s,"\n"); goto fail;
  1408.     }
  1409.        log2("info msg ",strnum3);
  1410.        strnum2[fmt_ulong(strnum2,(unsigned long) st.st_size)] = 0;
  1411.        log2(": bytes ",strnum2);
  1412.        log1(" from <"); logsafe(todoline.s + 1);
  1413.        strnum2[fmt_ulong(strnum2,pid)] = 0;
  1414.        log2("> qp ",strnum2);
  1415.        strnum2[fmt_ulong(strnum2,uid)] = 0;
  1416.        log2(" uid ",strnum2);
  1417.        log1("\n");
  1418.        break;
  1419.      case 'T':
  1420.        switch(rewrite(todoline.s + 1))
  1421.     {
  1422.      case 0: nomem(); goto fail;
  1423.      case 2: c = 1; break;
  1424.      default: c = 0; break;
  1425.         }
  1426.        if (fdchan[c] == -1)
  1427.     {
  1428.      fnmake_chanaddr(id,c);
  1429.      fdchan[c] = open_excl(fn.s);
  1430.      if (fdchan[c] == -1)
  1431.           { log3("warning: unable to create ",fn.s,"\n"); goto fail; }
  1432.      substdio_fdbuf(&sschan[c]
  1433.        ,write,fdchan[c],todobufchan[c],sizeof(todobufchan[c]));
  1434.      flagchan[c] = 1;
  1435.     }
  1436.        if (substdio_bput(&sschan[c],rwline.s,rwline.len) == -1)
  1437.         {
  1438.      fnmake_chanaddr(id,c);
  1439.          log3("warning: trouble writing to ",fn.s,"\n"); goto fail;
  1440.         }
  1441.        break;
  1442.      default:
  1443.        fnmake_todo(id);
  1444.        log3("warning: unknown record type in ",fn.s,"\n"); goto fail;
  1445.     }
  1446.   }
  1447.  
  1448.  close(fd); fd = -1;
  1449.  
  1450.  fnmake_info(id);
  1451.  if (substdio_flush(&ssinfo) == -1)
  1452.   { log3("warning: trouble writing to ",fn.s,"\n"); goto fail; }
  1453.  if (fsync(fdinfo) == -1)
  1454.   { log3("warning: trouble fsyncing ",fn.s,"\n"); goto fail; }
  1455.  close(fdinfo); fdinfo = -1;
  1456.  
  1457.  for (c = 0;c < CHANNELS;++c)
  1458.    if (fdchan[c] != -1)
  1459.     {
  1460.      fnmake_chanaddr(id,c);
  1461.      if (substdio_flush(&sschan[c]) == -1)
  1462.       { log3("warning: trouble writing to ",fn.s,"\n"); goto fail; }
  1463.      if (fsync(fdchan[c]) == -1)
  1464.       { log3("warning: trouble fsyncing ",fn.s,"\n"); goto fail; }
  1465.      close(fdchan[c]); fdchan[c] = -1;
  1466.     }
  1467.  
  1468.  fnmake_todo(id);
  1469.  if (substdio_putflush(&sstoqc,fn.s,fn.len) == -1) { cleandied(); return; }
  1470.  if (substdio_get(&ssfromqc,&ch,1) != 1) { cleandied(); return; }
  1471.  if (ch != '+')
  1472.   {
  1473.    log3("warning: qmail-clean unable to clean up ",fn.s,"\n");
  1474.    return;
  1475.   }
  1476.  
  1477.  pe.id = id; pe.dt = now();
  1478.  for (c = 0;c < CHANNELS;++c)
  1479.    if (flagchan[c])
  1480.      while (!prioq_insert(&pqchan[c],&pe)) nomem();
  1481.  
  1482.  for (c = 0;c < CHANNELS;++c) if (flagchan[c]) break;
  1483.  if (c == CHANNELS)
  1484.    while (!prioq_insert(&pqdone,&pe)) nomem();
  1485.  
  1486.  return;
  1487.  
  1488.  fail:
  1489.  if (fd != -1) close(fd);
  1490.  if (fdinfo != -1) close(fdinfo);
  1491.  for (c = 0;c < CHANNELS;++c)
  1492.    if (fdchan[c] != -1) close(fdchan[c]);
  1493. }
  1494.  
  1495.  
  1496. /* this file is too long ---------------------------------------------- MAIN */
  1497.  
  1498. int getcontrols() { if (control_init() == -1) return 0;
  1499.  if (control_readint(&lifetime,"control/queuelifetime") == -1) return 0;
  1500.  if (control_readint(&concurrency[0],"control/concurrencylocal") == -1) return 0;
  1501.  if (control_readint(&concurrency[1],"control/concurrencyremote") == -1) return 0;
  1502.  if (control_rldef(&envnoathost,"control/envnoathost",1,"envnoathost") != 1) return 0;
  1503.  if (control_rldef(&bouncefrom,"control/bouncefrom",0,"MAILER-DAEMON") != 1) return 0;
  1504.  if (control_rldef(&bouncehost,"control/bouncehost",1,"bouncehost") != 1) return 0;
  1505.  if (control_rldef(&doublebouncehost,"control/doublebouncehost",1,"doublebouncehost") != 1) return 0;
  1506.  if (control_rldef(&doublebounceto,"control/doublebounceto",0,"postmaster") != 1) return 0;
  1507.  if (!stralloc_cats(&doublebounceto,"@")) return 0;
  1508.  if (!stralloc_cat(&doublebounceto,&doublebouncehost)) return 0;
  1509.  if (!stralloc_0(&doublebounceto)) return 0;
  1510.  if (control_readfile(&locals,"control/locals",1) != 1) return 0;
  1511.  if (!constmap_init(&maplocals,locals.s,locals.len,0)) return 0;
  1512.  switch(control_readfile(&percenthack,"control/percenthack",0))
  1513.   {
  1514.    case -1: return 0;
  1515.    case 0: if (!constmap_init(&mappercenthack,"",0,0)) return 0; break;
  1516.    case 1: if (!constmap_init(&mappercenthack,percenthack.s,percenthack.len,0)) return 0; break;
  1517.   }
  1518.  switch(control_readfile(&redir,"control/recipientmap",0))
  1519.   {
  1520.    case -1: return 0;
  1521.    case 0: if (!constmap_init(&mapredir,"",0,1)) return 0; break;
  1522.    case 1: if (!constmap_init(&mapredir,redir.s,redir.len,1)) return 0; break;
  1523.   }
  1524.  switch(control_readfile(&vdoms,"control/virtualdomains",0))
  1525.   {
  1526.    case -1: return 0;
  1527.    case 0: if (!constmap_init(&mapvdoms,"",0,1)) return 0; break;
  1528.    case 1: if (!constmap_init(&mapvdoms,vdoms.s,vdoms.len,1)) return 0; break;
  1529.   }
  1530.  return 1; }
  1531.  
  1532. stralloc newlocals = {0};
  1533. stralloc newvdoms = {0};
  1534.  
  1535. void regetcontrols()
  1536. {
  1537.  int r;
  1538.  
  1539.  if (control_readfile(&newlocals,"control/locals",1) != 1)
  1540.   { log1("alert: unable to reread control/locals\n"); return; }
  1541.  r = control_readfile(&newvdoms,"control/virtualdomains",0);
  1542.  if (r == -1)
  1543.   { log1("alert: unable to reread control/virtualdomains\n"); return; }
  1544.  
  1545.  constmap_free(&maplocals);
  1546.  constmap_free(&mapvdoms);
  1547.  
  1548.  while (!stralloc_copy(&locals,&newlocals)) nomem();
  1549.  while (!constmap_init(&maplocals,locals.s,locals.len,0)) nomem();
  1550.  
  1551.  if (r)
  1552.   {
  1553.    while (!stralloc_copy(&vdoms,&newvdoms)) nomem();
  1554.    while (!constmap_init(&mapvdoms,vdoms.s,vdoms.len,1)) nomem();
  1555.   }
  1556.  else
  1557.    while (!constmap_init(&mapvdoms,"",0,1)) nomem();
  1558. }
  1559.  
  1560. void reread()
  1561. {
  1562.  if (chdir(auto_qmail) == -1)
  1563.   {
  1564.    log1("alert: unable to reread controls: unable to switch to home directory\n");
  1565.    return;
  1566.   }
  1567.  regetcontrols();
  1568.  while (chdir("queue") == -1)
  1569.   {
  1570.    log1("alert: unable to switch back to queue directory; HELP! sleeping...\n");
  1571.    sleep(10);
  1572.   }
  1573. }
  1574.  
  1575. void main()
  1576. {
  1577.  int fd;
  1578.  datetime_sec wakeup;
  1579.  fd_set rfds;
  1580.  fd_set wfds;
  1581.  int nfds;
  1582.  struct timeval tv;
  1583.  int c;
  1584. #ifdef __amigaos__
  1585.  long signalmask;
  1586.  int selret;
  1587. #endif
  1588.  
  1589.  if (chdir(auto_qmail) == -1)
  1590.   { log1("alert: cannot start: unable to switch to home directory\n"); _exit(111); }
  1591.  if (!getcontrols())
  1592.   { log1("alert: cannot start: unable to read controls\n"); _exit(111); }
  1593.  if (chdir("queue") == -1)
  1594.   { log1("alert: cannot start: unable to switch to queue directory\n"); _exit(111); }
  1595. #ifdef __amigaos__
  1596.  /* Clear the signals we use before use. */
  1597.  SetSignal (0UL, SIGBREAKF_CTRL_D | SIGBREAKF_CTRL_E | SIGBREAKF_CTRL_F);
  1598.  /* Cause Ctrl-C (from e.g. the Break command) do terminate qmail.
  1599.   * Ctrl-C is mapped to SIGINT by default, so map SIGINT to SIGTERM). */
  1600.  signal (SIGINT, sigterm);
  1601. #endif
  1602.  sig_pipeignore();
  1603.  sig_termcatch(sigterm);
  1604.  sig_alarmcatch(sigalrm);
  1605.  sig_hangupcatch(sighup);
  1606.  sig_childdefault();
  1607.  umask(077);
  1608.  
  1609.  fd = open_write("lock/sendmutex");
  1610.  if (fd == -1)
  1611.   { log1("alert: cannot start: unable to open mutex\n"); _exit(111); }
  1612.  if (lock_exnb(fd) == -1)
  1613.   { log1("alert: cannot start: qmail-send is already running\n"); _exit(111); }
  1614.  
  1615.  numjobs = 0;
  1616.  for (c = 0;c < CHANNELS;++c)
  1617.   {
  1618.    char ch;
  1619.    int u;
  1620.    int r;
  1621.    do
  1622.      r = read(chanfdin[c],&ch,1);
  1623.    while ((r == -1) && (errno == error_intr));
  1624.    if (r < 1)
  1625.     { log1("alert: cannot start: hath the daemon spawn no fire?\n"); _exit(111); }
  1626.    u = (unsigned int) (unsigned char) ch;
  1627.    if (concurrency[c] > u) concurrency[c] = u;
  1628.    numjobs += concurrency[c];
  1629.   }
  1630.  
  1631.  log1("running\n");
  1632.  
  1633.  fnmake_init();
  1634.  
  1635.  for (c = 0;c < CHANNELS;++c)
  1636.    if (!concurrency[c])
  1637.      log1(channodelmsg[c]);
  1638.  
  1639.  comm_init();
  1640.  
  1641.  pqstart();
  1642.  job_init();
  1643.  del_init();
  1644.  pass_init();
  1645.  todo_init();
  1646.  cleanup_init();
  1647.  
  1648.  while (!flagexitasap || !del_canexit())
  1649.   {
  1650.    if (flagexitasap) del_saywhynoexit();
  1651.  
  1652.    recent = now();
  1653.  
  1654.    if (flagrunasap) { flagrunasap = 0; pqrun(); }
  1655.    if (flagreadasap) { flagreadasap = 0; reread(); }
  1656.  
  1657.    wakeup = recent + SLEEP_FOREVER;
  1658.    FD_ZERO(&rfds);
  1659.    FD_ZERO(&wfds);
  1660.    nfds = 1;
  1661.  
  1662.    comm_selprep(&nfds,&wfds);
  1663.    del_selprep(&nfds,&rfds);
  1664.    pass_selprep(&wakeup);
  1665.    todo_selprep(&nfds,&rfds,&wakeup);
  1666.    cleanup_selprep(&wakeup);
  1667.  
  1668.    if (wakeup <= recent) tv.tv_sec = 0;
  1669.    else tv.tv_sec = wakeup - recent + SLEEP_FUZZ;
  1670.    tv.tv_usec = 0;
  1671.  
  1672. #ifdef __amigaos__
  1673.    signalmask = SIGBREAKF_CTRL_C | SIGBREAKF_CTRL_D |
  1674.                 SIGBREAKF_CTRL_E | SIGBREAKF_CTRL_F;
  1675.    selret = ix_select (nfds, &rfds, &wfds, (fd_set *) 0UL, &tv, &signalmask);
  1676.    if (selret == -1 && !(signalmask & SIGBREAKF_CTRL_D))
  1677. #else
  1678.    if (select(nfds,&rfds,&wfds,(fd_set *) 0,&tv) == -1)
  1679. #endif
  1680.      if (errno == error_intr)
  1681. #ifndef __amigaos__
  1682.        ;
  1683. #else
  1684.      {
  1685.        /* Map some Amiga signals to Un*x ones
  1686.           (Ctrl-C is mapped to SIGINT elsewhere). */
  1687.        if (signalmask & SIGBREAKF_CTRL_E) sigalrm ();
  1688.        if (signalmask & SIGBREAKF_CTRL_F) sighup ();
  1689. #endif /* __amigaos__ */
  1690.      }
  1691.      else
  1692.        log1("warning: trouble in select\n");
  1693.    else
  1694.     {
  1695.      recent = now();
  1696.  
  1697. #ifdef __amigaos__
  1698.      if (selret == -1 && (signalmask & SIGBREAKF_CTRL_D))
  1699.      {
  1700.        /* This prevents qmail-send from hanging (but don't ask me why). */
  1701.        FD_ZERO (&rfds);
  1702.        FD_ZERO (&wfds);
  1703.      }
  1704. #endif
  1705.      comm_do(&wfds);
  1706.      del_do(&rfds);
  1707. #ifdef __amigaos__
  1708.      todo_do(signalmask);
  1709. #else
  1710.      todo_do(&rfds);
  1711. #endif
  1712.      pass_do();
  1713.      cleanup_do();
  1714.     }
  1715.   }
  1716.  pqfinish();
  1717.  log1("exiting\n");
  1718.  _exit(0);
  1719. }
  1720.